module TransactionService::Gateway
  class PaypalAdapter < GatewayAdapter

    DataTypes = PaypalService::API::DataTypes

    def implements_process(process)
      [:none, :preauthorize].include?(process)
    end

    def create_payment(tx:, gateway_fields:, force_sync:)
      create_payment_info = DataTypes.create_create_payment_request(
        {
         transaction_id: tx.id,
         payment_action: :authorization,
         item_name: "#{tx.listing_title} (Listing ID: #{tx.listing_id})",
         item_quantity: tx.listing_quantity,
         item_price: tx.unit_price,
         merchant_id: tx.listing_author_id,
         require_shipping_address: tx.delivery_method == 'shipping',
         shipping_total: tx.shipping_price,
         order_total: order_total(tx),
         success: gateway_fields[:success_url],
         cancel: gateway_fields[:cancel_url],
         merchant_brand_logo_url: gateway_fields[:merchant_brand_logo_url]})

      result = paypal_api.payments.request(
        tx.community_id,
        create_payment_info,
        force_sync: force_sync)

      unless result[:success]
        return SyncCompletion.new(result)
      end

      if result[:data][:process_token].present?
        # PayPal API performed the operation asynchronously
        AsyncCompletion.new(Result::Success.new({ process_token: result[:data][:process_token] }))
      else
        # PayPal API performed the operation synchronously
        AsyncCompletion.new(Result::Success.new({ redirect_url: result[:data][:redirect_url] }))
      end
    end

    def reject_payment(tx:, reason: "")
      AsyncCompletion.new(paypal_api.payments.void(tx.community_id, tx.id, {note: reason}))
    end

    def complete_preauthorization(tx:)
      AsyncCompletion.new(
        paypal_api.payments.get_payment(tx.community_id, tx.id)
        .and_then { |payment|
          paypal_api.payments.full_capture(
            tx.community_id,
            tx.id,
            DataTypes.create_payment_info({ payment_total: payment[:authorization_total] }))
        })
    end

    def get_payment_details(tx:)
      payment = paypal_api.payments.get_payment(tx.community_id, tx.id).maybe

      payment_total = payment[:payment_total].or_else(nil)
      total_price = Maybe(payment[:payment_total].or_else(payment[:authorization_total].or_else(nil)))
                    .or_else(order_total(tx))

      { payment_total: payment_total,
        total_price: total_price,
        charged_commission: payment[:commission_total].or_else(nil),
        payment_gateway_fee: payment[:fee_total].or_else(nil) }
    end

    private

    def paypal_api
      PaypalService::API::Api
    end

    def order_total(tx)
      # Note: Quantity may be confusing in Paypal Checkout page, thus,
      # we don't use separated unit price and quantity, only the total
      # price for now.

      shipping_total = Maybe(tx.shipping_price).or_else(0)
      tx.unit_price * tx.listing_quantity + shipping_total
    end
  end

end
